const bcrypt = require('bcryptjs')
import { Table, Column, DataType } from 'sequelize-typescript'
import { ScopeTypeEnum } from '@app/lib/enum'
import { BaseModel } from '@core/db'

interface UserAttributes {
  phone: string;
  password: string;
  openid?: string;
  nickname?: string;
  auth?: number;
}

@Table({
  tableName: 'user',
  comment: '用户',
})
export default class UserModel extends BaseModel<UserModel, UserAttributes> {
  @Column({
    type: DataType.INTEGER,
    primaryKey: true, // 设置主键
    autoIncrement: true, // 自增加
  })
  declare id: number

  @Column({
    type: DataType.STRING,
    comment: '用户昵称',
  })
  declare nickname: string

  @Column({
    type: DataType.STRING,
    comment: '手机号',
  })
  declare phone: string

  @Column({
    type: DataType.STRING,
    set(val: string): void {
      const salt = bcrypt.genSaltSync(10) // 10表示生成密码的成本
      const psw = bcrypt.hashSync(val, salt)
      this.setDataValue('password', psw)
    },
    comment: '密码（已加密）',
  })
  declare password: string

  @Column({
    type: DataType.STRING(64),
    unique: true,
    comment: '微信openid',
  })
  declare openid: string

  @Column({
    type: DataType.INTEGER,
    defaultValue: ScopeTypeEnum.UserScope, // 1为user(普通用户)， 2为admin(普通管理员)， 3为super(超级管理员)
    comment: '权限级别(数字)',
  })
  declare auth: number

  /**
   * auth(数字)对应的权限scope(字符串)
   * @returns {String} 用户权限
   */
  get scope() {
    return ScopeTypeEnum[(this as any).auth]
  }

  keys(): void {
    // 显示scope和created_at属性，其中scope是getter属性，created_at是默认隐藏属性
    // 隐藏password和openid属性
    this.append('scope', 'created_at').hide('password', 'openid')
  }

  /**
   * 验证密码
   * @param password 待验证的密码
   */
  validatePassword(password: string): boolean {
    // 使用 getDataValue 获取原始密码值
    const hashedPassword = this.getDataValue('password');
    return bcrypt.compareSync(password, hashedPassword);
  }

  /**
   * 校验用户登录注册信息
   * @param phone 手机号
   * @param password 密码
   * @param authCode 授权码
   * @returns 用户信息
   */
  static async validateParams(phone: string, password: string, authCode?: string) {
    if (!phone) throw new global.errs.ParameterException('手机号不能为空');
    if (!/^\d{11}$/.test(phone)) throw new global.errs.ParameterException('手机号格式错误');

    const user = await UserModel.findOne({ where: { phone } });
    // !!authCode = 注册 -> 授权码校验
    if (authCode) {
      if (user) throw new global.errs.ParameterException('手机号已注册，请直接登录');
      if (authCode.length !== 6) throw new global.errs.ParameterException('授权码为6位字符');
      if (authCode !== 'HPJ888') throw new global.errs.ParameterException('授权码错误');
    } else {
      // 登录 -> 用户校验
      if (!user) throw new global.errs.APIException('当前用户不存在，请先注册');

      // 密码校验
      if (!user.validatePassword(password)) throw new global.errs.ParameterException('密码错误');
    }

    if (!password) throw new global.errs.ParameterException('请输入密码');
    if (!/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{6,}$/.test(password)) throw new global.errs.ParameterException('密码须包含字母和数字，且长度至少6位');

    return user ? user?.toJSON() : null;
  }

  /**
   * 注册用户
   * @param phone 手机号
   * @param password 密码
   * @returns 用户信息
   */
  static async registerByPhone(phone: string, password: string) {
    const user = await UserModel.create({
      phone,
      password,
      nickname: `用户${Math.random().toString(36).slice(-6).toUpperCase()}`,
      auth: ScopeTypeEnum.UserScope
    });
    if (!user) throw new global.errs.APIException('注册失败');

    return user?.toJSON();
  }
}
